#pragma once

#include "typedefs.h"
#include "controls.h"
#include <boost/shared_ptr.hpp>

namespace outline {

template <typename T> class segmap;

struct bspline_opt_params {
	uint iterations;
	float sampling_max_distance;
	fit_mode fit;
	transform_type global_transform;
	bool antiloop;
	bspline_opt_params() { }
	bspline_opt_params(uint iterations_, float sampling_max_distance_, fit_mode fit_, transform_type global_transform_, bool antiloop_) :
		iterations(iterations_), sampling_max_distance(sampling_max_distance_), fit(fit_), global_transform(global_transform_), antiloop(antiloop_) { }
};

//bspline_opt is a framework used for bringing (optimizing) bspline near some given contour
template <typename R>
class bspline_opt_base {
public:
	typedef R real;
	typedef std::vector<real> real_vector;
	typedef types::point<R> point;
	typedef std::vector<point> point_vector;
private:
	class variable_base;
	class variable;
	class variable_impl;
	class center;
	class tangent;
	class tangents;
	class node;
	class fixed_center;
	class mobile_center;
	class arbitrary_tangent;
	class zero_tangent;
	class fixed_tangent;
	class independent_tangents;
	class zero_tangents;
	class fixed_relength_tangents;
	class composite_node;
	typedef boost::shared_ptr<center> center_sptr;
	typedef boost::shared_ptr<tangent> tangent_sptr;
	typedef boost::shared_ptr<tangents> tangents_sptr;
	typedef boost::shared_ptr<node> node_sptr;
	typedef std::vector<node_sptr> node_vector;
private:
	class uvariable;
	class ucenter;
	class utangent;
	class utangents;
	class unode;
	template <class C, class B> class sep_base;
	template <class C> class sep_center;
	typedef sep_center<fixed_center> fixed_ucenter;
	typedef sep_center<mobile_center> mobile_ucenter;
	template <class C> class sep_tangent;
	typedef sep_tangent<zero_tangent> zero_utangent;
	typedef sep_tangent<arbitrary_tangent> arbitrary_utangent;
	class fixed_utangent;
	class fixed_direction_tangent;
	class independent_utangents;
	class zero_utangents;
	class fixed_relength_utangents;
	class fixed_direction_fixed_relength_utangents;
	class composite_unode;
	typedef boost::shared_ptr<ucenter> ucenter_sptr;
	typedef boost::shared_ptr<utangent> utangent_sptr;
	typedef boost::shared_ptr<utangents> utangents_sptr;
	typedef boost::shared_ptr<unode> unode_sptr;
	typedef std::vector<unode_sptr> unode_vector;
protected:
	class spline;
	class spline2;
	class uspline;
	template <class C> class gspline;
};

template <typename T, typename R>
class bspline_opt : public bspline_opt_base<R> {
public:
	static void doit(
		//in
		segmap<T> const & target,
		std::vector<junc_type> const & junc, ///< required type of each junction in poly-Bezier
		bspline_opt_params const & params,
		//in & out
		controls<R> & ctrls, ///< size = 3*(the number of pieces) + 1
		//optional in
		//attraction: each node and control point may have attractor to which it's pulled with relative force 'traction'
		std::vector<point> const * attractors = 0,
		std::vector<R> const * tractions = 0, //0 <= traction <= 1; 0 is absence of traction, 1 - control is fixed in attractor
		std::vector<int> const * fixed_dir_tangents = 0, ///< each component here is a bitmap and it describes a node (junction)
		//optional out for debugging
		std::vector<point> * poles = 0, ///< points on the target contour
		std::vector<point> * samples = 0 ///< corresponding points on the final spline 
	);
private:
	class append_samples_n_poles;
	template <class S> class append_eq;
private:
	static void core(
		segmap<T> const & target,
		std::vector<junc_type> const & junc,
		bspline_opt_params const & params,
		controls<R> & ctrls,
		std::vector<point> const * attractors,
		std::vector<R> const * tractions,
		std::vector<int> const * fixed_dir_tangents,
		std::vector<point> * poles,
		std::vector<point> * samples
	);
	template <class C> static void global_fit(
		segmap<T> const & target,
		std::vector<junc_type> const & junc,
		bspline_opt_params const & params,
		controls<R> & ctrls,
		std::vector<R> const * tractions
	);
};

} //namespace outline
